﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using AzukiMap.Tools;
using AzukiMap.Tools.Logger;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using Geometries = GisSharpBlog.NetTopologySuite.Geometries;

namespace AzukiMap.Request
{
    public class PostGisRasterIndexParser : PostGisVectorParser
    {
        private RasterIndexRecord[] _records;

        public PostGisRasterIndexParser(BinaryReader geomReader)
           : base(geomReader)
        {
#if SILVERLIGHT
            Logger.Debug(
                "PostGisRasterIndexParser.PostGisRasterIndexParser(): "
                + " geomReader.BaseStreamLength: " + geomReader.BaseStream.Length
                );
#endif
        }

        public RasterIndexRecord[] Records
        {
            get { return _records; }
        }

        /// <summary>
        /// The respose header is only a number. 
        /// If the number is positive value　or zero, it means number of records. 
        /// If it has minus value, it is a error code. 
        /// </summary>
        protected override void ParseHeader()
        {
#if SILVERLIGHT
            if (_geomReader.BaseStream.Position != 0)
            {
                Logger.Error("PostGisDataParser: Reader position is not head.");
                throw new Exception("Reader position is not head.");
            }
#endif
            int header = _geomReader.ReadInt32();
            // has data
            if (header >= 0)
            {
                _numOfObjects = header;
                _errorCode = 0;
                _records = new RasterIndexRecord[_numOfObjects];

                Logger.Debug(
                    "PostGisDataParser.ParseHeader(): "
                    + " header = " + header.ToString()
                    );

            }
            // has error
            else
            {
                _numOfObjects = 0;
                _errorCode = header;
                _records = new RasterIndexRecord[] { };

                Logger.Debug(
                    "PostGisDataParser.ParseHeader(): No Records."
                    + " header = " + header.ToString()
                    );
            }
        }


        RasterIndexRecord GetNextRecord()
        {
            if (HasNext())
            {
                var record = new RasterIndexRecord();
                //PostGisRecord record = new PostGisRecord();
                var nouse1 = GetIntFromBinRead();
                var nouse2 = GetIntFromBinRead();
                record.RasterIndex = GetAttributes()[0];
                record.Extent = GetReversePolygon((IMultiPolygon)GetGeometryFromBinReader());
                _currentReadingRecord++;
                return record;
                //record.RasterIndex = GetRasterIndex();
                //record.Extent = (Geometries.Polygon)GetGeometryFromBinReader();

                //_currentReadingRecord++;
                //return record;
            }
            else
            {
                Logger.Warn("PostGisDataParser: There is no record to read in the stream.");
                throw new NoNextRecordException("There is no record to read in the stream.");
            }
        }

        IMultiPolygon GetReversePolygon(IMultiPolygon mPoly)
        {
            var geomUtil = new GeometryUtil();
            return geomUtil.CreateMultiPolygon(GetPolygonsFromMulti(mPoly).ToArray());
        }

        IEnumerable<IPolygon>  GetPolygonsFromMulti(IMultiPolygon mPoly)
        {
            //return from IPolygon poly in mPoly select GetReversePolygon(poly);
            foreach( var poly in mPoly)
            {
                if ( poly.GetType().Equals(typeof(Polygon)))
                {
                    yield return GetReversePolygon((IPolygon)poly);
                }
            }


        }

        IPolygon GetReversePolygon(IPolygon poly)
        {
            if ( poly == null )
            {
                return null;
            }
            var geomUtil = new GeometryUtil();
            return geomUtil.CreateExtentPolygon(poly.EnvelopeInternal.MinX, poly.EnvelopeInternal.MinY,
                                         poly.EnvelopeInternal.MaxX, poly.EnvelopeInternal.MaxY);

        }

        protected override void ParseRecords()
        {
            if (_errorCode == 0)
            {
                while (HasNext())
                {
                    Records[_currentReadingRecord] =
                        GetNextRecord();
                }
            }
        }

        public new bool HasNext()
        {
            if (Records == null)
            {
                return false;
            }
            return (_currentReadingRecord < _numOfObjects);
        }

        string GetRasterIndex()
        {
            return _geomReader.ReadString();
        }

        #region IEnumerable Members

        protected IEnumerator GetEnumerator()
        {
            return new RasterIndexEnum(Records);
        }

        #endregion
    }

    public class RasterIndexEnum : IEnumerator
    {
        RasterIndexRecord[] _records;
        int _pos = -1;
        bool _disposed = false;
       
        public RasterIndexEnum(RasterIndexRecord[] records)
        {
            _records = records;
        }

        #region IEnumerator Members

        public object Current
        {
            get
            {
                try
                {
                    return _records[_pos];
                }
                catch (IndexOutOfRangeException ex)
                {
                    Logger.Error("PostGisRasterIndexEnum: IndexOutOfRangeException.", ex);
                    throw new InvalidOperationException();
                }
            }
        }

        public bool MoveNext()
        {
            _pos++;
            return (_pos < _records.Length);
        }

        public void Reset()
        {
            _pos = -1;
        }

        #endregion
    }

    public class RasterIndexRecord
    {
        public int Id { get; set; }
        public string RasterIndex { get; set; }
        public IMultiPolygon Extent { get; set; }
        public List<FrameworkElement> Images { get; set; }

        private int _zindex;
        private Visibility _visibility;
        private double _opacity;

        public RasterIndexRecord ()
        {
            Images = new List<FrameworkElement>();
            _zindex = 0;
            _visibility = Visibility.Visible;
            _opacity = 1;
        }

        public int ZIndex
        {
            set
            {
                _zindex = value;
                foreach (var element in Images)
                {
                    element.SetValue(Canvas.ZIndexProperty, value);
                }
            }
            get
            {
                return _zindex;
            }
        }

        public Visibility Visibility
        {
            set
            {
                _visibility = value;
                foreach (var element in Images)
                {
                    element.Visibility = value;
                }
            }
            get
            {
                return _visibility;
            }
        }

        public double Opacity
        {
            set
            {
                _opacity = value;
                foreach (var element in Images)
                {
                    element.Opacity = value;
                }
            }

            get
            {
                return _opacity;
            }
        }

    }
}
